﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestcraftLibrary.CVar
{
    public class CVar
    {
        private object value;
        private CVarFlags flags;

        public string Name { get; private set; }
        public string Description { get; private set; }
        public bool IsImmutable { get { return (this.flags & CVarFlags.CVarFlag_Immutable) == CVarFlags.CVarFlag_Immutable; } }
        public bool IsPersistent { get { return (this.flags & CVarFlags.CVarFlag_Persistent) == CVarFlags.CVarFlag_Persistent; } }

        private CVar(string name, string desc, object value, CVarFlags flags)
        {
            this.Name = name;
            this.Description = desc;
            this.value = value;
            this.flags = flags;
        }

        internal static CVar DeserializeFromStream(System.IO.BinaryReader reader)
        {
            CVar result = null;

            object value;
            string name = reader.ReadString();
            string desc = reader.ReadString();
            CVarFlags flags = (CVarFlags)reader.ReadUInt16();
            if ((flags & CVarFlags.CVarFlag_Bool) == CVarFlags.CVarFlag_Bool)
                value = reader.ReadBoolean();
            else if ((flags & CVarFlags.CVarFlag_Float) == CVarFlags.CVarFlag_Float)
                value = reader.ReadSingle();
            else if ((flags & CVarFlags.CVarFlag_Int) == CVarFlags.CVarFlag_Int)
                value = reader.ReadInt32();
            else if ((flags & CVarFlags.CVarFlag_String) == CVarFlags.CVarFlag_String)
                value = reader.ReadString();
            else
                throw new System.Runtime.Serialization.SerializationException("Invalid flags in CVar");

            result = new CVar(name, desc, value, flags);

            return result;
        }

        internal void SerializeToStream(System.IO.BinaryWriter writer)
        {
            writer.Write(this.Name);
            writer.Write(this.Description);
            writer.Write((UInt16)this.flags);

            if ((flags & CVarFlags.CVarFlag_Bool) == CVarFlags.CVarFlag_Bool)
                writer.Write((bool)this.value);
            else if ((flags & CVarFlags.CVarFlag_Float) == CVarFlags.CVarFlag_Float)
                writer.Write((float)this.value);
            else if ((flags & CVarFlags.CVarFlag_Int) == CVarFlags.CVarFlag_Int)
                writer.Write((int)this.value);
            else if ((flags & CVarFlags.CVarFlag_String) == CVarFlags.CVarFlag_String)
                writer.Write((string)this.value);
            else
                throw new System.Runtime.Serialization.SerializationException("Invalid flags in CVar");
        }

        public CVar(string name, string desc, float value, bool persistent)
            : this(name, desc, value, CVarFlags.CVarFlag_Float)
        {
        }

        public CVar(string name, string desc, int value, bool persistent)
            : this(name, desc, value, CVarFlags.CVarFlag_Int)
        {
        }

        public CVar(string name, string desc, string value, bool persistent)
            : this(name, desc, value, CVarFlags.CVarFlag_String)
        {
        }

        public CVar(string name, string desc, bool value, bool persistent)
            : this(name, desc, value, CVarFlags.CVarFlag_Bool | (persistent ? CVarFlags.CVarFlag_Persistent : 0))
        {
        }

        public bool GetBool()
        {
            if ((~this.flags & CVarFlags.CVarFlag_Bool) == CVarFlags.CVarFlag_Bool)
                throw new InvalidOperationException(string.Format("CVar '{0}' is not of type bool.", this.Name));

            return (bool)this.value;
        }

        public void SetBool(bool value)
        {
            if ((~this.flags & CVarFlags.CVarFlag_Bool) == CVarFlags.CVarFlag_Bool)
                throw new InvalidOperationException("The value of this CVar is not bool.");
            if ((this.flags & CVarFlags.CVarFlag_Immutable) == CVarFlags.CVarFlag_Immutable)
                throw new InvalidOperationException("This CVar is immutable and can not be mopdified.");

            this.value = value;
        }

        public int GetInt()
        {
            if ((~this.flags & CVarFlags.CVarFlag_Bool) == CVarFlags.CVarFlag_Int)
                throw new InvalidOperationException(string.Format("CVar '{0}' is not of type int.", this.Name));

            return (int)this.value;
        }

        public void SetInt(int value)
        {
            if ((~this.flags & CVarFlags.CVarFlag_Bool) == CVarFlags.CVarFlag_Int)
                throw new InvalidOperationException("The value of this CVar is not int.");
            if ((this.flags & CVarFlags.CVarFlag_Immutable) == CVarFlags.CVarFlag_Immutable)
                throw new InvalidOperationException("This CVar is immutable and can not be mopdified.");

            this.value = value;
        }

        public float GetFloat()
        {
            if ((~this.flags & CVarFlags.CVarFlag_Float) == CVarFlags.CVarFlag_Float)
                throw new InvalidOperationException(string.Format("CVar '{0}' is not of type float.", this.Name));

            return (float)this.value;
        }

        public void SetFloat(float value)
        {
            if ((~this.flags & CVarFlags.CVarFlag_Float) == CVarFlags.CVarFlag_Float)
                throw new InvalidOperationException("The value of this CVar is not float.");
            if ((this.flags & CVarFlags.CVarFlag_Immutable) == CVarFlags.CVarFlag_Immutable)
                throw new InvalidOperationException("This CVar is immutable and can not be mopdified.");

            this.value = value;
        }

        public string GetString()
        {
            if ((~this.flags & CVarFlags.CVarFlag_String) == CVarFlags.CVarFlag_String)
                throw new InvalidOperationException(string.Format("CVar '{0}' is not of type string.", this.Name));

            return (string)this.value;
        }

        public void SetString(string value)
        {
            if ((~this.flags & CVarFlags.CVarFlag_String) == CVarFlags.CVarFlag_String)
                throw new InvalidOperationException("The value of this CVar is not string.");
            if ((this.flags & CVarFlags.CVarFlag_Immutable) == CVarFlags.CVarFlag_Immutable)
                throw new InvalidOperationException("This CVar is immutable and can not be mopdified.");

            this.value = value;
        }

        public string GetValueAsString()
        {
            return this.value.ToString();
        }

        public void SetValueAsString(string value)
        {
            bool success = false;
            if ((this.flags & CVarFlags.CVarFlag_Bool) == CVarFlags.CVarFlag_Bool)
            {
                bool parsedValue;
                success = bool.TryParse(value, out parsedValue);
                if (success)
                    this.SetBool(parsedValue);
            }
            else if ((this.flags & CVarFlags.CVarFlag_Float) == CVarFlags.CVarFlag_Float)
            {
                float parsedValue;
                success = float.TryParse(value, out parsedValue);
                if(success)
                    this.SetFloat(parsedValue);
            }
            else if ((this.flags & CVarFlags.CVarFlag_Int) == CVarFlags.CVarFlag_Int)
            {
                int parsedValue;
                success = int.TryParse(value, out parsedValue);
                if(success)
                    this.SetInt(parsedValue);
            }
            else if ((this.flags & CVarFlags.CVarFlag_String) == CVarFlags.CVarFlag_String)
            {
                this.SetString(value);
                success = true;
            }

            if (!success)
                throw new InvalidOperationException(string.Format("Could not assign value '{0}' to CVar."));
        }
    }
}
